/*
 * @Description: 文件添加和读取
 * @Author: 听风
 * @Date: 2020-12-22 13:29:54
 */

const fs = require('fs');
const stat = fs.stat;
const downloadGit = require('download-git-repo');
const chalk = require('chalk');
const symbols = require('log-symbols');
const Handlebars = require('handlebars');
const { basePath } = require('../config');
const { askPrompt } = require('./tool');
const temPause = require('../configs/template');

/**
 * @description: 创建文件夹
 * @param {*} path
 * @return {*}
 */
const createPath = path => {
  /**
   * ！！！注意
   * 此处需要修改为递归创建
   */
  if (!fs.existsSync(path)) {
    fs.mkdirSync(path);
  }
  return true;
};

const createPathOnRoot = path => {
  /**
   * ！！！注意
   * 此处需要修改为递归创建
   */
  const realPath = (path[0] === '/' ? basePath : '') + path;
  if (!fs.existsSync(realPath)) {
    fs.mkdirSync(realPath);
  }
  return true;
};

/**
 * @description: 判断文件是否存在
 * @param {*} path
 * @param {*} log
 * @return {*}
 */
const existsSync = (path, log = true) => {
  const res = fs.existsSync(path);
  if (log && !res) {
    console.log(symbols.error, chalk.red(`${path}文件不存在`));
  }
  return res;
};

const existsRootSync = (path, log = true) => {
  const res = fs.existsSync(`${basePath}/${path}`);
  if (log && !res) {
    console.log(symbols.error, chalk.red(`${path}文件不存在`));
  }
  return res;
};

/**
 * @description: 删除目录及其所有文件
 * @param {*} _path
 * @param {*} _savePath
 * @return {*}
 */
const delDirOnRoot = (_path, _savePath) => {
  const path = `${basePath}/${_path}`,
    savePath = `${basePath}/${_savePath}`;
  return new Promise((resolve, reject) => {
    let files = [];
    if (existsSync(path)) {
      files = fs.readdirSync(path);
      files.forEach((file, index) => {
        const curPath = path + '/' + file;
        if (fs.statSync(curPath).isDirectory()) {
          delDirOnRoot(curPath, savePath); //递归删除文件夹
        } else {
          if (_savePath && createPath(savePath)) {
            // 转移文件，转移文件前校验目标目录，不存在则创建
            fs.renameSync(curPath, `${savePath}/${file}`);
          } else {
            fs.unlinkSync(curPath); //删除文件
          }
        }
      });
      fs.rmdirSync(path);
      resolve();
    } else {
      reject('文件夹不存在');
    }
  });
};

/**
 * @description: 路径检查，不存在的文件夹则创建
 * @param {*} filepath
 * @param {*} rootPath
 * @return {*}
 */
const patchPath = (filepath, rootPath) => {
  if (filepath.includes('/')) {
    // 拿到每个文件夹名（去掉最后的文件名）
    const dirs = filepath.split('/');
    dirs.pop();
    // 依次创建文件夹
    const finalDir = dirs.reduce((prev, cur) => {
      createPath(`${rootPath}/${prev}`);
      return `${prev}/${cur}`;
    });
    createPath(`${rootPath}/${finalDir}`);
  }
};

/**
 * @description: 从远程Git仓库下载模版文件
 * @param {*} branch
 * @param {*} savePath
 * @return {*}
 */
const downloadGitTemp = (branch, savePath) => {
  const temp = 'ttf-cli_temp';
  return new Promise((resolve, reject) => {
    downloadGit(
      `https://gitee.com:linktodream/ttf-cli-templates#${branch}`,
      temp,
      { clone: true },
      err => {
        if (err) {
          reject(err);
        } else {
          // 转移并删除临时文件夹
          delDirOnRoot(temp, savePath);
          resolve(savePath);
        }
      }
    );
  });
};

/**
 * @description: 读取文件
 * @param {*} filename
 * @return {*} promise
 */
const readFileFromRoot = filename => {
  return new Promise((resolve, reject) => {
    fs.readFile(`${basePath}/${filename}`, 'utf-8', (err, data) => {
      if (err) {
        reject(err);
      } else {
        resolve(data);
      }
    });
  });
};

/**
 * @description: 在命令执行的根目录创建文件
 * @param {*} filename
 * @return {*} promise
 */
const writeFileOnRoot = (filename, content = '') => {
  return new Promise((resolve, reject) => {
    // 路径检查
    patchPath(filename, basePath);
    fs.writeFile(`${basePath}/${filename}`, content, err => {
      if (err) {
        reject(err);
        return err;
      }
      resolve();
    });
  });
};

/**
 * @description: 删除文件
 * @param {*} filename
 * @return {*}
 */
const delFileFromRoot = filename => {
  return new Promise((resolve, reject) => {
    // 判断目录/文件是否存在
    if (!existsSync(filename)) {
      reject();
      return;
    }
    fs.unlink(filename, error => {
      if (error) {
        reject(error);
        return error;
      }
      resolve();
    });
  });
};

/**
 * @description: 重命名文件
 * @param {*} oldFile
 * @param {*} newFile
 * @return {*}
 */
const renameFileOnRoot = (oldFile, newFile) => {
  return new Promise((resolve, reject) => {
    fs.rename(oldFile, newFile, error => {
      if (error) {
        reject(error);
        return false;
      }
      resolve();
    });
  });
};

/**
 * @description: 给文件追加内容
 * @param {*} filepath
 * @param {*} data
 * @return {*}
 */
const appendFileOnRoot = (filepath, data) => {
  try {
    fs.appendFileSync(`${basePath}/${filepath}`, data);
  } catch (err) {
    /* 处理错误 */
    console.log(symbols.error, chalk.red(`写入错误：${err}`));
  }
};

/**
 * @description: 复制目录中的所有文件包括子目录
 * @param {*} src 需要复制的目录
 * @param {*} path 复制到指定的目录
 * @return {*}
 */
const copyPathOnRoot = (src, path) => {
  const dst = basePath + (path[0] === '/' ? '' : '/') + path;
  createPath(dst);
  // 读取目录中的所有文件/目录
  fs.readdir(src, (err, paths) => {
    if (err) {
      throw err;
    }

    paths.forEach(path => {
      const _src = src + '/' + path;
      const _dst = dst + '/' + path;
      let readable, writable;
      stat(_src, (err, st) => {
        if (err) {
          console.log(_src, st);
          throw err;
        }

        // 判断是否为文件
        if (st.isFile()) {
          // 创建读取流
          readable = fs.createReadStream(_src);

          // 创建写入流
          writable = fs.createWriteStream(_dst);

          // 通过管道来传输流
          readable.pipe(writable);
        }

        // 如果是目录则递归调用自身
        else if (st.isDirectory()) {
          if (!existsSync(_dst, false)) {
            createPath(_dst);
          }
          copyPathOnRoot(_src, _dst);
        }
      });
    });
  });
};

/**
 * @description: 复制文件到指定目录
 * @param {*} filepath 需要复制的文件路径
 * @param {*} dst 复制到指定的目录
 * @param {*} filename 指定文件名
 * @return {*}
 */
const copyFileOnRoot = (filepath, dst, filename) => {
  const lastPath = filepath.split('/').reverse()[0];
  const dot = lastPath.split('.').reverse()[0];
  const name = dot ? lastPath : filename || `${new Date().valueOf()}.${dot}`;
  patchPath(dst, basePath);
  const content = fs.readFileSync(filepath);
  writeFileOnRoot(`${dst}/${filename || name}`, content);
};

const changeSpinnerText = (spinner, text) => {
  spinner && (spinner.text = text);
};

/**
 * @description: 从git上下载指定分支的文件
 * @param {*} branch 需要下载的文件分支
 * @param {*} savePath 下载到指定的目录
 * @param {*} filename 指定文件名
 * @return {*}
 */
const downloadGitFile = async (branch, savePath, filename, { spinner, isCover, meta = {} }) => {
  const filePath = `${savePath === '/' ? '' : savePath + '/'}`;
  const fileLocation = `${filePath}${filename}`;
  changeSpinnerText(spinner, `加载${filename}`);
  // 下载并覆盖文件
  const coverFile = async () => {
    spinner.start('正在加载文件……\n\r');
    await downloadGitTemp(branch, savePath);
    await renameFileOnRoot(temPause(branch), fileLocation);
    const content = await readFileFromRoot(fileLocation);

    // 渲染模板
    const file = Handlebars.compile(content)(meta);
    await writeFileOnRoot(fileLocation, file);
  };
  if (existsRootSync(fileLocation, false) && !isCover) {
    spinner.stop();
    // 询问是否覆盖
    const config = [
      {
        type: 'input',
        name: 'cover',
        message: `${filename}已存在，是否覆盖（y/n）？`,
        default: 'y',
      },
    ];
    const condition = {
      verify({ cover }) {
        return ['y', 'n'].includes(cover);
      },
      msg: '请输入y或n',
    };
    // 文件存在则询问是否覆盖
    const coverFlag = (await askPrompt(config, condition)).cover === 'y';
    if (coverFlag) {
      await coverFile();
    } else {
      return;
    }
  } else {
    await coverFile();
  }
  changeSpinnerText(spinner, `${filename}加载完成`);
};

module.exports = {
  downloadGitTemp,
  readFileFromRoot,
  writeFileOnRoot,
  delFileFromRoot,
  renameFileOnRoot,
  existsSync,
  existsRootSync,
  appendFileOnRoot,
  copyPathOnRoot,
  copyFileOnRoot,
  downloadGitFile,
};
